package cn.itcast.orders.service.impl;

import cn.itcast.cart.dto.ShoppingCartDTO;
import cn.itcast.cart.feign.CartClient;
import cn.itcast.cart.pojo.ShoppingCart;
import cn.itcast.constant.MessageConstant;
import cn.itcast.context.BaseContext;
import cn.itcast.exception.OrderBusinessException;
import cn.itcast.orders.dto.*;
import cn.itcast.orders.mapper.AddressBookMapper;
import cn.itcast.orders.mapper.OrderDetailMapper;
import cn.itcast.orders.mapper.OrderMapper;
import cn.itcast.orders.pojo.AddressBook;
import cn.itcast.orders.pojo.OrderDetail;
import cn.itcast.orders.pojo.Orders;
import cn.itcast.orders.service.OrderService;
import cn.itcast.orders.socket.WebSocketServer;
import cn.itcast.orders.vo.*;
import cn.itcast.result.PageBean;
import cn.itcast.sys.feign.SysClient;
import cn.itcast.users.feign.UsersClient;
import cn.itcast.users.pojo.User;
import cn.itcast.utils.WeChatPayUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.example.BaiduMapUtil;
import com.example.entity.Location;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.stream.Collectors;


/**
 * 订单
 */
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private AddressBookMapper addressBookMapper;

    @Autowired
    private CartClient cartClient;

    @Autowired
    private UsersClient usersClient;

    @Autowired
    private WeChatPayUtil weChatPayUtil;

    @Autowired
    private BaiduMapUtil baiduMapUtil;

    @Autowired
    private WebSocketServer webSocketServer;

    @Autowired
    private SysClient sysClient;


    /**
     * 用户下单
     *
     * @param ordersSubmitDTO
     * @return
     */
    @Transactional
    @Override
    public OrderSubmitVO submit(OrdersSubmitDTO ordersSubmitDTO) {

        // 判断购物车是否有商品
        List<ShoppingCart> carts = cartClient.list(ShoppingCart.builder().userId(BaseContext.getCurrentId()).build());
        if (carts == null || carts.size() == 0) throw new OrderBusinessException(MessageConstant.SHOPPING_CART_IS_NULL);

        // 判断是否有地址信息
        if (ordersSubmitDTO.getAddressBookId() == null)
            throw new OrderBusinessException(MessageConstant.ADDRESS_BOOK_IS_NULL);
        AddressBook addressBook = addressBookMapper.getById(ordersSubmitDTO.getAddressBookId());
        if (addressBook == null) throw new OrderBusinessException(MessageConstant.ADDRESS_BOOK_IS_NULL);

        // 获取商家和订单地址距离
        StringBuilder sb = new StringBuilder();
        sb.append(addressBook.getProvinceName());
        sb.append(addressBook.getCityName());
        sb.append(addressBook.getDistrictName());
        sb.append(addressBook.getDetail());
        String userAddress = sb.toString();
        Location location = null;
        String distance = null;
        try {
            location = baiduMapUtil.getLocation(userAddress);
            distance = baiduMapUtil.getDistance(location);
        } catch (Exception e) {
            // 获取配送地址距离异常
            throw new OrderBusinessException(MessageConstant.THE_SHIPPING_ADDRESS_DOES_NOT_EXIST);
        }

        //判断配送地址是否超出配送范围
        if (distance.isEmpty() || Integer.valueOf(distance) > 5000) {
            throw new OrderBusinessException(MessageConstant.OUT_OF_RANGE);
        }


        User user = usersClient.getById(BaseContext.getCurrentId());

        Orders orders = new Orders();
        BeanUtils.copyProperties(ordersSubmitDTO, orders);
        orders.setNumber("T" + System.currentTimeMillis()); //订单号
        orders.setStatus(Orders.PENDING_PAYMENT); //订单状态
        orders.setUserId(BaseContext.getCurrentId()); //下单用户id
        orders.setOrderTime(LocalDateTime.now()); //下单时间
        orders.setPayStatus(Orders.UN_PAID); //支付状态
        orders.setUserName(user.getName()); //用户名
        orders.setPhone(addressBook.getPhone()); //手机号
        orders.setAddress(addressBook.getDetail()); //地址
        orders.setConsignee(addressBook.getConsignee()); //收货人

        orderMapper.insert(orders); //主键自增返回

        List<OrderDetail> orderDetails = carts.stream().map(e -> {
            OrderDetail orderDetail = new OrderDetail();
            BeanUtils.copyProperties(e, orderDetail);
            orderDetail.setOrderId(orders.getId());
            return orderDetail;
        }).collect(Collectors.toList());

        orderDetailMapper.insertBatch(orderDetails);

        // 清空购物车
        cartClient.delete(ShoppingCart.builder().userId(BaseContext.getCurrentId()).build());

        return OrderSubmitVO.builder()
                .id(orders.getId())
                .orderAmount(orders.getAmount())
                .orderNumber(orders.getNumber())
                .orderTime(orders.getOrderTime())
                .build();
    }


    /**
     * 订单支付
     *
     * @param ordersPaymentDTO
     * @return
     */
    public OrderPaymentVO payment(OrdersPaymentDTO ordersPaymentDTO) throws Exception {
        // 当前登录用户id
        Long userId = BaseContext.getCurrentId();
        User user = usersClient.getById(userId);

        //调用微信支付接口，生成预支付交易单
        JSONObject jsonObject = weChatPayUtil.pay(
                ordersPaymentDTO.getOrderNumber(), //商户订单号
                new BigDecimal(0.01), //支付金额，单位 元
                "苍穹外卖订单", //商品描述
                user.getOpenid() //微信用户的openid
        );

        if (jsonObject.getString("code") != null && jsonObject.getString("code").equals("ORDERPAID")) {
            throw new OrderBusinessException("该订单已支付");
        }

        OrderPaymentVO vo = jsonObject.toJavaObject(OrderPaymentVO.class);
        vo.setPackageStr(jsonObject.getString("package"));

        return vo;
    }

    /**
     * 支付成功，修改订单状态
     *
     * @param outTradeNo
     */
    public void paySuccess(String outTradeNo) {
        // 根据订单号查询订单
        Orders ordersDB = orderMapper.getByNumber(outTradeNo);

        // 根据订单id更新订单的状态、支付方式、支付状态、结账时间
        Orders orders = Orders.builder()
                .id(ordersDB.getId())
                .status(Orders.TO_BE_CONFIRMED)
                .payStatus(Orders.PAID)
                .checkoutTime(LocalDateTime.now())
                .build();

        orderMapper.update(orders);

        //基于WebSocket实现来单提醒
        Map map = new HashMap();
        map.put("type", 1);//消息类型，1表示来单提醒
        map.put("orderId", orders.getId());
        map.put("content", "订单号：" + orders.getNumber());

        webSocketServer.sendToAllClient(JSON.toJSONString(map));
    }

    /**
     * 查询历史订单
     *
     * @param ordersPageQueryDTO
     * @return
     */
    @Override
    public PageBean historyOrders(OrdersPageQueryDTO ordersPageQueryDTO) {

        // 开启分页
        PageHelper.startPage(ordersPageQueryDTO.getPage(), ordersPageQueryDTO.getPageSize());
        ordersPageQueryDTO.setUserId(BaseContext.getCurrentId());
        Page<OrderVO> page = orderMapper.selectQuery(ordersPageQueryDTO);

        return PageBean.builder()
                .total(page.getTotal())
                .records(page.getResult())
                .build();
    }

    /**
     * 查询订单详情
     *
     * @param id
     * @return
     */
    @Override
    public OrderVO getOrderDetailById(Long id) {
        return orderMapper.getOrderDetailById(id);
    }

    /**
     * 取消订单
     *
     * @param ordersCancelDTO
     * @param isAdmin
     */
    @Override
    public void cancelOrder(OrdersCancelDTO ordersCancelDTO, boolean isAdmin) {
        OrderVO orderVO = orderMapper.getOrderDetailById(ordersCancelDTO.getId());
        // 判断订单是否存在
        if (orderVO == null) {
            throw new OrderBusinessException(MessageConstant.ORDER_NOT_FOUND);
        }
        Integer status = orderVO.getStatus();
        Orders orders = Orders.builder()
                .id(ordersCancelDTO.getId())
                .cancelTime(LocalDateTime.now()) // 取消时间
                .cancelReason(ordersCancelDTO.getCancelReason())
                .status(Orders.CANCELLED) // 更新订单状态为已取消
                .build();

        //判断订单状态是否为待付款
        if (status == Orders.PENDING_PAYMENT) {
            orderMapper.update(orders);
            return;
        }

        //判断订单状态是否为待接单或是管理端相关请求且订单支付状态为已付款
        if (status == Orders.TO_BE_CONFIRMED
                || (status == Orders.CONFIRMED
                || status == Orders.DELIVERY_IN_PROGRESS
                || status == Orders.COMPLETED
                && isAdmin == true)
                && orderVO.getPayStatus() == Orders.PAID) {
            // 退款请求
//            weChatPayUtil.refund();

            orders.setPayStatus(Orders.REFUND); // 更新支付状态为已退款
            orderMapper.update(orders);
            return;
        }

        // 若订单为其他状态则抛出异常
        throw new OrderBusinessException(MessageConstant.ORDER_STATUS_ERROR);
    }

    /**
     * 再来一单
     *
     * @param id
     */
    @Transactional
    @Override
    public void repetition(Long id) {
        OrderVO orderVO = orderMapper.getOrderDetailById(id);
        List<OrderDetail> orderDetails = orderVO.getOrderDetailList();

        orderDetails.forEach(e -> {

            ShoppingCartDTO shoppingCartDTO = new ShoppingCartDTO();
            BeanUtils.copyProperties(e, shoppingCartDTO);

            cartClient.add(shoppingCartDTO);
        });

    }

    /**
     * 订单搜索
     *
     * @param ordersPageQueryDTO
     * @return
     */
    @Override
    public PageBean conditionSearch(OrdersPageQueryDTO ordersPageQueryDTO) {
        PageHelper.startPage(ordersPageQueryDTO.getPage(), ordersPageQueryDTO.getPageSize());
        Page<OrderVO> page = orderMapper.selectQuery(ordersPageQueryDTO);

        StringJoiner sj = new StringJoiner(";");
        page.getResult().forEach(e -> {
            sj.setEmptyValue(""); //清空StringJoiner
            e.getOrderDetailList().forEach(od -> {
                sj.add(od.getName() + "*" + od.getNumber());
            });
            e.setOrderDishes(sj.toString());
        });

        return PageBean.builder()
                .total(page.getTotal())
                .records(page.getResult())
                .build();
    }

    /**
     * 各个状态的订单数量统计
     *
     * @return
     */
    @Override
    public OrderStatisticsVO statistics() {
        return orderMapper.getStatistics();
    }

    /**
     * 接单
     *
     * @param id
     */
    @Override
    public void confirm(Long id) {
        OrderVO orderVO = orderMapper.getOrderDetailById(id);
        //判断订单状态是否为待接单
        if (orderVO != null && orderVO.getStatus() == Orders.TO_BE_CONFIRMED) {
            //更新订单状态为已接单
            orderMapper.update(Orders.builder()
                    .id(id)
                    .status(Orders.CONFIRMED)
                    .build());
            return;
        }
        //订单状态非待接单，抛出异常
        throw new OrderBusinessException(MessageConstant.ORDER_STATUS_ERROR);
    }

    /**
     * 拒单
     *
     * @param ordersRejectionDTO
     */
    @Override
    public void rejection(OrdersRejectionDTO ordersRejectionDTO) {
        OrderVO orderVO = orderMapper.getOrderDetailById(ordersRejectionDTO.getId());
        //判断订单状态是否为待接单
        if (orderVO != null && orderVO.getStatus() == Orders.TO_BE_CONFIRMED) {
            // 退款请求
//            weChatPayUtil.refund();

            //退款成功后更新订单信息
            orderMapper.update(Orders.builder()
                    .id(ordersRejectionDTO.getId())
                    .rejectionReason(ordersRejectionDTO.getRejectionReason()) // 订单拒绝原因
                    .cancelTime(LocalDateTime.now()) // 取消时间
                    .status(Orders.CANCELLED) // 更新订单状态为已取消
                    .payStatus(Orders.REFUND) // 更新支付状态为已退款
                    .build());
            return;
        }
        //订单状态非待接单，抛出异常
        throw new OrderBusinessException(MessageConstant.ORDER_STATUS_ERROR);
    }

    /**
     * 派送订单
     *
     * @param id
     */
    @Override
    public void delivery(Long id) {
        OrderVO orderVO = orderMapper.getOrderDetailById(id);
        //判断订单状态是否为已接单
        if (orderVO != null && orderVO.getStatus() == Orders.CONFIRMED) {
            //更新订单状态为派送中
            orderMapper.update(Orders.builder()
                    .id(id)
                    .status(Orders.DELIVERY_IN_PROGRESS)
                    .build());
            return;
        }
        //订单状态非已接单，抛出异常
        throw new OrderBusinessException(MessageConstant.ORDER_STATUS_ERROR);
    }

    /**
     * 完成订单
     *
     * @param id
     */
    @Override
    public void complete(Long id) {
        OrderVO orderVO = orderMapper.getOrderDetailById(id);
        //判断订单状态是否为派送中
        if (orderVO != null && orderVO.getStatus() == Orders.DELIVERY_IN_PROGRESS) {
            //更新订单状态为已完成
            orderMapper.update(Orders.builder()
                    .id(id)
                    .status(Orders.COMPLETED)
                    .build());
            return;
        }
        //订单状态非派送中，抛出异常
        throw new OrderBusinessException(MessageConstant.ORDER_STATUS_ERROR);
    }

    /**
     * 用户催单
     *
     * @param id
     */
    @Override
    public void reminder(Long id) {
        OrderVO orderVO = orderMapper.getOrderDetailById(id);
        if (orderVO == null) throw new OrderBusinessException(MessageConstant.ORDER_NOT_FOUND);

        //基于WebSocket实现催单
        Map map = new HashMap();
        map.put("type", 2);//2代表用户催单
        map.put("orderId", id);
        map.put("content", "订单号：" + orderVO.getNumber());

        webSocketServer.sendToAllClient(JSON.toJSONString(map));
    }

    /**
     * 营业额统计
     *
     * @param begin
     * @param end
     * @return
     */
    @Override
    public TurnoverReportVO turnoverStatistics(LocalDate begin, LocalDate end) {
        List<String> dateList = new ArrayList<>(); // 日期列表
        List<Double> turnoverList = new ArrayList<>(); // 营业额列表


        while (!begin.isAfter(end)) {

            Map<String, Object> map = new HashMap<>();
            map.put("start", LocalDateTime.of(begin, LocalTime.MIN));
            map.put("end", LocalDateTime.of(begin, LocalTime.MAX));
            map.put("status", Orders.COMPLETED);
            Double turnover = orderMapper.getTurnoverReportByMap(map);

            dateList.add(begin.toString());
            turnoverList.add(turnover);

            begin = begin.plusDays(1);
        }


        return TurnoverReportVO.builder()
                .dateList(StringUtils.join(dateList, ","))
                .turnoverList(StringUtils.join(turnoverList, ","))
                .build();
    }

    /**
     * 订单统计接口
     *
     * @param begin
     * @param end
     * @return
     */
    @Override
    public OrderReportVO ordersStatistics(LocalDate begin, LocalDate end) {
        List<String> dateList = new ArrayList<>(); // 日期列表
        List<Integer> orderCountList = new ArrayList<>(); // 每日订单数
        List<Integer> validOrderCountList = new ArrayList<>(); // 每日有效订单数


        while (!begin.isAfter(end)) {

            Map<String, Object> map = new HashMap<>();
            map.put("start", LocalDateTime.of(begin, LocalTime.MIN));
            map.put("end", LocalDateTime.of(begin, LocalTime.MAX));
            map.put("status", null);
            Integer orderCount = orderMapper.getOrdersReportByMap(map);


            map.put("status", Orders.COMPLETED);
            Integer validOrderCount = orderMapper.getOrdersReportByMap(map);

            dateList.add(begin.toString());
            orderCountList.add(orderCount);
            validOrderCountList.add(validOrderCount);

            begin = begin.plusDays(1);
        }


        Integer totalOrderCount = orderCountList.stream().mapToInt(e -> e).sum();
        Integer validOrderCount = validOrderCountList.stream().mapToInt(e -> e).sum();
        Double orderCompletionRate = totalOrderCount == 0 ? 0 : BigDecimal.valueOf(validOrderCount)
                .divide(BigDecimal.valueOf(totalOrderCount), 4, RoundingMode.DOWN)
                .doubleValue();

        return OrderReportVO.builder()
                .dateList(StringUtils.join(dateList, ","))
                .orderCountList(StringUtils.join(orderCountList, ","))
                .validOrderCountList(StringUtils.join(validOrderCountList, ","))
                .totalOrderCount(totalOrderCount)
                .validOrderCount(validOrderCount)
                .orderCompletionRate(orderCompletionRate)
                .build();
    }

    /**
     * 查询销量排名top10接口
     *
     * @param begin
     * @param end
     * @return
     */
    @Override
    public SalesTop10ReportVO top10(LocalDate begin, LocalDate end) {

        Map<String, Object> map = new HashMap<>();
        map.put("start", LocalDateTime.of(begin, LocalTime.MIN));
        map.put("end", LocalDateTime.of(end, LocalTime.MAX));
        map.put("status", Orders.COMPLETED);
        List<GoodsSalesDTO> list = orderMapper.getSalesTop10(map);

        String nameList = list.stream().map(e -> e.getName()).collect(Collectors.joining(",")); // 商品名称列表
        String numberList = list.stream().map(e -> e.getNumber().toString()).collect(Collectors.joining(",")); // 销量列表


        return SalesTop10ReportVO.builder()
                .nameList(nameList)
                .numberList(numberList)
                .build();
    }

    /**
     * 导出Excel报表接口
     *
     * @param response
     */
    @SneakyThrows
    @Override
    public void export(HttpServletResponse response) {
        // 查询近30天数据
        LocalDateTime start = LocalDateTime.of(LocalDate.now().minusDays(30), LocalTime.MIN);
        LocalDateTime end = LocalDateTime.of(LocalDate.now().minusDays(1), LocalTime.MAX);

        Map<String, LocalDateTime> requestData = new HashMap<>();
        requestData.put("start", start);
        requestData.put("end", end);
        Map<String, Map<String, Object>> map = sysClient.businessDataMap(requestData);

        InputStream is = this.getClass().getClassLoader().getResourceAsStream("templates/运营数据报表模板.xlsx");
        XSSFWorkbook excel = new XSSFWorkbook(is);
        XSSFSheet sheet = excel.getSheetAt(0);
        sheet.getRow(1).getCell(1).setCellValue(start.toLocalDate() + "至" + end.toLocalDate());

        for (int i = 0; i < 30; i++) {
            LocalDate date = start.plusDays(i).toLocalDate();

            Map<String, Object> v = map.get(date.toString());
            //日期
            sheet.getRow(7 + i).getCell(1).setCellValue(date.toString());
            //营业额
            sheet.getRow(7 + i).getCell(2).setCellValue("￥" + v.get("turnover").toString());
            //有效订单数
            sheet.getRow(7 + i).getCell(3).setCellValue(v.get("validOrderCount").toString());
            //订单完成率
            sheet.getRow(7 + i).getCell(4).setCellValue(v.get("orderCompletionRate").toString() + "%");
            //平均客单价
            sheet.getRow(7 + i).getCell(5).setCellValue("￥" + v.get("unitPrice").toString());
            //新增用户数
            sheet.getRow(7 + i).getCell(6).setCellValue(v.get("newUsers").toString());
        }

        Map<String, Object> totalData = new HashMap<>();
        map.forEach((k, v) -> {
            //总营业额
            totalData.put("totalTurnover", Double.valueOf(v.get("turnover").toString()) + Double.valueOf(totalData.getOrDefault("totalTurnover", 0.00).toString()));
            //总有效订单数
            totalData.put("totalValidOrderCount", Integer.valueOf(v.get("validOrderCount").toString()) + Integer.valueOf(totalData.getOrDefault("totalValidOrderCount", 0).toString()));
            //总新增用户数
            totalData.put("totalNewUsers", Integer.valueOf(v.get("newUsers").toString()) + Integer.valueOf(totalData.getOrDefault("totalNewUsers", 0).toString()));
            //总订单总数
            totalData.put("totalOrders", Integer.valueOf(v.get("ordersCount").toString()) + Integer.valueOf(totalData.getOrDefault("totalOrders", 0).toString()));
        });

        //计算总订单完成率
        Integer totalValidOrderCount = Integer.valueOf(totalData.get("totalValidOrderCount").toString()); //总有效订单数
        Integer totalOrders = Integer.valueOf(totalData.get("totalOrders").toString()); //总订单总数

        String totalOrderCompletionRate = totalOrders == 0 ? "0.00%" :
                BigDecimal.valueOf(totalValidOrderCount)
                        .divide(BigDecimal.valueOf(totalOrders), 2, RoundingMode.HALF_UP)
                        .multiply(BigDecimal.valueOf(100)) + "%";
        //计算总平均客单价
        Double totalTurnover = Double.valueOf(totalData.get("totalTurnover").toString()); //总营业额

        String totalUnitPrice = totalValidOrderCount == 0 ? "￥0.00" : "￥" +
                BigDecimal.valueOf(totalTurnover)
                        .divide(BigDecimal.valueOf(totalValidOrderCount), 2, RoundingMode.HALF_UP);

        //填充数据
        sheet.getRow(3).getCell(2).setCellValue(totalData.get("totalTurnover").toString());
        sheet.getRow(3).getCell(4).setCellValue(totalOrderCompletionRate);
        sheet.getRow(3).getCell(6).setCellValue(totalData.get("totalNewUsers").toString());
        sheet.getRow(4).getCell(2).setCellValue(totalData.get("totalValidOrderCount").toString());
        sheet.getRow(4).getCell(4).setCellValue(totalUnitPrice);

        ServletOutputStream os = response.getOutputStream();
        excel.write(os);

        os.flush();
        os.close();
        excel.close();
    }

}